Пример #1
0
def extract_correspondences(dataset, source_path, target_path,
                            n_correspondences):
    """
    Prepares the arguments and runs the correspondence extration in parallel mode

    Args:
        dataset (str): name of the dataset
        source_path (str): path to the raw files
        target_path (str): path to where the extracted data will be saved
        n_correspondences (int): number of points to sample

    """
    source_path = os.path.join(source_path, dataset, 'raw_data')

    scene_paths = get_folder_list(source_path)
    idx = list(range(len(scene_paths)))

    pool = mp.Pool(processes=6)
    func = partial(run_correspondence_extraction, dataset, source_path,
                   target_path, n_correspondences)
    pool.map(func, idx)
    pool.close()
    pool.join()
Пример #2
0
    def __init__(self, args):

        self.files = []
        self.root = args.source_path
        self.use_mutuals = args.mutuals
        save_path = os.path.join(self.root, 'results', args.method)
        save_path += '/mutuals/' if args.mutuals else '/all/'

        logging.info("Loading the eval data from {}!".format(self.root))

        scene_names = get_folder_list(
            os.path.join(self.root, 'correspondences'))

        for folder in scene_names:
            curr_scene_name = folder.split('/')[-1]
            if os.path.exists(
                    os.path.join(save_path, curr_scene_name,
                                 'traj.txt')) and not args.overwrite:
                logging.info(
                    'Trajectory for scene {} already exists and will not be recomputed.'
                    .format(curr_scene_name))
            else:
                if args.only_gt_overlaping:
                    gt_pairs, gt_traj = read_trajectory(
                        os.path.join(self.root, 'raw_data', curr_scene_name,
                                     "gt.log"))
                    for idx_1, idx_2, _ in gt_pairs:
                        self.files.append(
                            os.path.join(
                                folder, curr_scene_name + '_{}_{}.npz'.format(
                                    str(idx_1).zfill(3),
                                    str(idx_2).zfill(3))))

                else:
                    corr_files = get_file_list(folder)
                    for corr in corr_files:
                        self.files.append(corr)
Пример #3
0
def extract_features_batch(model, source_path, target_path, dataset,
                           voxel_size, device):
    """
    Extracts the per point features in the FCGF feature space and saves them to the predefined path

    Args:
        model (FCGF model instance): model used to inferr the descriptors
        source_path (str): path to the raw files
        target path (str): where to save the extracted data
        dataset (float): name of the dataset
        voxel_size (float): voxel sized used to create the sparse tensor
        device (pytorch device): cuda or cpu
    """

    source_path = os.path.join(source_path, dataset, 'raw_data')
    target_path = os.path.join(target_path, dataset, 'features')

    ensure_dir(target_path)

    folders = get_folder_list(source_path)

    assert len(folders) > 0, 'Could not find {} folders under {}'.format(
        dataset, source_path)

    logging.info(folders)
    list_file = os.path.join(target_path, 'list.txt')
    f = open(list_file, 'w')
    model.eval()

    for fo in folders:
        scene_name = fo.split()
        files = get_file_list(fo, '.ply')
        fo_base = os.path.basename(fo)
        ensure_dir(os.path.join(target_path, fo_base))

        f.write('%s %d\n' % (fo_base, len(files)))
        for i, fi in enumerate(files):
            save_fn = '%s_%03d' % (fo_base, i)
            if os.path.exists(
                    os.path.join(target_path, fo_base, save_fn + '.npz')):
                print(
                    'Correspondence file already exits moving to the next example.'
                )
            else:
                # Extract features from a file
                pcd = o3d.io.read_point_cloud(fi)

                if i % 100 == 0:
                    logging.info(f'{i} / {len(files)}: {save_fn}')

                xyz_down, feature = extract_features(model,
                                                     xyz=np.array(pcd.points),
                                                     rgb=None,
                                                     normal=None,
                                                     voxel_size=voxel_size,
                                                     device=device,
                                                     skip_check=True)

                np.savez_compressed(os.path.join(target_path, fo_base,
                                                 save_fn),
                                    points=np.array(pcd.points),
                                    xyz=xyz_down,
                                    feature=feature.detach().cpu().numpy())

    f.close()
Пример #4
0
def extract_precomputed_training_data(dataset, source_path, target_path,
                                      voxel_size, inlier_threshold):
    """
    Prepares the data for training the filtering networks with precomputed correspondences (without FCGF descriptor) 

    Args:
        dataset (str): name of the dataset
        source_path (str): path to the raw data
        target_path (str): path to where the extracted data will be saved
        voxel_size (float): voxel size that was used to compute the features
        inlier_threshold (float): threshold to determine if a correspondence is an inlier or outlier
    """
    source_path = os.path.join(source_path, dataset, 'raw_data')
    features_path = os.path.join(target_path, dataset, 'features')
    correspondence_path = os.path.join(target_path, dataset, 'correspondences')
    target_path = os.path.join(target_path, dataset, 'training_data')

    ensure_dir(target_path)

    # Check that the GT global transformation matrices are available and that the FCGF features are computed
    folders = get_folder_list(source_path)

    assert len(folders) > 0, 'Could not find {} folders under {}'.format(
        dataset, source_path)

    logging.info('Found {} scenes from the {} dataset!'.format(
        len(folders), dataset))

    for fo in folders:

        scene_name = fo.split()
        fo_base = os.path.basename(fo)
        ensure_dir(os.path.join(target_path, fo_base))

        pc_files = get_file_list(fo, '.ply')
        trans_files = get_file_list(fo, '.txt')
        assert len(pc_files) <= len(
            trans_files
        ), 'The number of point cloud files does not equal the number of GT trans parameters!'

        feat_files = get_file_list(os.path.join(features_path, fo_base),
                                   '.npz')
        assert len(pc_files) == len(
            feat_files
        ), 'Features for scene {} are either not computed or some are missing!'.format(
            fo_base)

        coor_files = get_file_list(os.path.join(correspondence_path, fo_base),
                                   '.npz')

        assert len(coor_files) == int(
            (len(feat_files) * (len(feat_files) - 1)) / 2
        ), 'Correspondence files for the scene {} are missing. First run the correspondence extraction!'.format(
            fo_base)

        # Loop over all fragment pairs and compute the training data
        for idx_1 in range(len(pc_files)):
            for idx_2 in range(idx_1 + 1, len(pc_files)):
                if os.path.exists(
                        os.path.join(
                            target_path, fo_base,
                            '{}_{}_{}.npz'.format(fo_base,
                                                  str(idx_1).zfill(3),
                                                  str(idx_2).zfill(3)))):
                    logging.info(
                        'Training file already exits moving to the next example.'
                    )

                data = np.load(
                    os.path.join(
                        correspondence_path, fo_base,
                        '{}_{}_{}.npz'.format(fo_base,
                                              str(idx_1).zfill(3),
                                              str(idx_2).zfill(3))))
                xs = data['xs']
                mutuals = data['mutuals']
                ratios = data['ratios']

                # Get the GT transformation parameters
                t_1 = np.genfromtxt(os.path.join(
                    source_path, fo_base,
                    'cloud_bin_{}.info.txt'.format(idx_1)),
                                    skip_header=1)
                t_2 = np.genfromtxt(os.path.join(
                    source_path, fo_base,
                    'cloud_bin_{}.info.txt'.format(idx_2)),
                                    skip_header=1)

                # Get the GT transformation parameters
                pc_1 = load_point_cloud(os.path.join(
                    source_path, fo_base, 'cloud_bin_{}.ply'.format(idx_1)),
                                        data_type='numpy')
                pc_2 = load_point_cloud(os.path.join(
                    source_path, fo_base, 'cloud_bin_{}.ply'.format(idx_2)),
                                        data_type='numpy')

                pc_1_tr = transform_point_cloud(pc_1, t_1[0:3, 0:3],
                                                t_1[0:3, 3].reshape(-1, 1))
                pc_2_tr = transform_point_cloud(pc_2, t_2[0:3, 0:3],
                                                t_2[0:3, 3].reshape(-1, 1))

                overlap_ratio = compute_overlap_ratio(pc_1_tr,
                                                      pc_2_tr,
                                                      np.eye(4),
                                                      method='FCGF',
                                                      voxel_size=voxel_size)

                # Estimate pairwise transformation parameters
                t_3 = np.matmul(np.linalg.inv(t_2), t_1)

                r_matrix = t_3[0:3, 0:3]
                t_vector = t_3[0:3, 3]

                # Transform the keypoints of the first point cloud
                pc_1_key_tr = transform_point_cloud(xs[:, 0:3], r_matrix,
                                                    t_vector.reshape(-1, 1))

                # Compute the residuals after the transformation
                y_s = np.sqrt(
                    np.sum(np.square(pc_1_key_tr - xs[:, 3:6]), axis=1))

                # Inlier percentage
                inlier_ratio = np.where(
                    y_s < inlier_threshold)[0].shape[0] / y_s.shape[0]
                inlier_ratio_mutuals = np.where(
                    y_s[mutuals.astype(bool).reshape(-1)] < inlier_threshold
                )[0].shape[0] / np.sum(mutuals)

                np.savez_compressed(os.path.join(
                    target_path, fo_base, 'cloud_{}_{}.npz'.format(
                        str(idx_1).zfill(3),
                        str(idx_2).zfill(3))),
                                    R=r_matrix,
                                    t=t_vector,
                                    x=xs,
                                    y=y_s,
                                    mutuals=mutuals,
                                    inlier_ratio=inlier_ratio,
                                    inlier_ratio_mutuals=inlier_ratio_mutuals,
                                    ratios=ratios,
                                    overlap=overlap_ratio)
Пример #5
0
def run_correspondence_extraction(dataset, source_path, target_path,
                                  n_correspondences, idx):
    """
    Computes the correspondences in the FCGF space together with the mutuals and ratios side information

    Args:
        dataset (str): name of the dataset
        source_path (str): path to the raw data
        target_path (str): path to where the extracted data will be saved
        n_correspondences (int): number of points to sample
        idx (int): index of the scene, used for parallel processing

    """

    # Initialize all the paths
    features_path = os.path.join(target_path, dataset, 'features')
    target_path = os.path.join(target_path, dataset, 'correspondences')

    fo = get_folder_list(source_path)[idx]
    fo_base = os.path.basename(fo)
    files = get_file_list(os.path.join(features_path, fo_base), '.npz')

    ensure_dir(os.path.join(target_path, fo_base))

    # Loop over all fragment pairs and compute the training data
    for idx_1 in range(len(files)):
        for idx_2 in range(idx_1 + 1, len(files)):
            if os.path.exists(
                    os.path.join(
                        target_path, fo_base,
                        '{}_{}_{}.npz'.format(fo_base,
                                              str(idx_1).zfill(3),
                                              str(idx_2).zfill(3)))):
                logging.info(
                    'Correspondence file already exits moving to the next example.'
                )

            else:
                pc_1_data = np.load(
                    os.path.join(
                        features_path, fo_base,
                        fo_base + '_{}.npz'.format(str(idx_1).zfill(3))))
                pc_1_features = pc_1_data['feature']
                pc_1_keypoints = pc_1_data['xyz']

                pc_2_data = np.load(
                    os.path.join(
                        features_path, fo_base,
                        fo_base + '_{}.npz'.format(str(idx_2).zfill(3))))
                pc_2_features = pc_2_data['feature']
                pc_2_keypoints = pc_2_data['xyz']

                # Sample with replacement if less then n_correspondences points are in the point cloud
                if pc_1_features.shape[0] >= n_correspondences:
                    inds_1 = np.random.choice(pc_1_features.shape[0],
                                              n_correspondences,
                                              replace=False)
                else:
                    inds_1 = np.random.choice(pc_1_features.shape[0],
                                              n_correspondences,
                                              replace=True)

                if pc_2_features.shape[0] >= n_correspondences:
                    inds_2 = np.random.choice(pc_2_features.shape[0],
                                              n_correspondences,
                                              replace=False)
                else:
                    inds_2 = np.random.choice(pc_2_features.shape[0],
                                              n_correspondences,
                                              replace=True)

                pc_1_features = pc_1_features[inds_1, :]
                pc_2_features = pc_2_features[inds_2, :]
                pc_1_key = pc_1_keypoints[inds_1, :]
                pc_2_key = pc_2_keypoints[inds_2, :]

                # find the correspondence using nearest neighbor search in the feature space (two way)
                nn_search = NearestNeighbors(n_neighbors=1,
                                             metric='minkowski',
                                             p=2)
                nn_search.fit(pc_2_features)
                nn_dists, nn_indices = nn_search.kneighbors(
                    X=pc_1_features, n_neighbors=2, return_distance=True)

                nn_search.fit(pc_1_features)
                nn_dists_1, nn_indices_1 = nn_search.kneighbors(
                    X=pc_2_features, n_neighbors=2, return_distance=True)

                ol_nn_ids = np.where(
                    (nn_indices[nn_indices_1[:, 0], 0] -
                     np.arange(pc_1_features.shape[0])) == 0)[0]

                # Initialize mutuals and ratios
                mutuals = np.zeros((n_correspondences, 1))
                mutuals[ol_nn_ids] = 1
                ratios = nn_dists[:, 0] / nn_dists[:, 1]

                # Concatenate the correspondence coordinates
                xs = np.concatenate(
                    (pc_1_key[nn_indices_1[:, 0], :], pc_2_key), axis=1)

                np.savez_compressed(os.path.join(
                    target_path, fo_base,
                    '{}_{}_{}.npz'.format(fo_base,
                                          str(idx_1).zfill(3),
                                          str(idx_2).zfill(3))),
                                    x=xs,
                                    mutuals=mutuals,
                                    ratios=ratios)